use async_trait::async_trait;
use common::base::{
    dll_api::{
        AddChannelIdToRemoteServicesApi, BeginTxApi, BuildChannelApi, CommitApi, ExecuteApi,
        FileApi, GenIDApi, GetAllConnIdApi, GetAllLocalServiceApi, GetChannelIdByConnIdApi,
        GetConnectionApi, GetHttpDataApi, GetXPRCPortApi, IsTableExistApi, LoadServiceApi,
        OutputLogApi, PutbackConnectionApi, QueryApi, RemoveHttpClientApi,
        RemoveRemoteServiceAllChannelIdApi, ResponseApi, RollbackApi, SendHttpRequestApi,
        SendMessageApi, SetChannelXRPCPortApi, SleepApi, UnLoadServiceApi, WriteApi,
    },
    status::Status,
};

#[cfg(unix)]
use libloading::os::unix::Symbol;

#[cfg(windows)]
use libloading::os::windows::Symbol;

use tokio::sync::oneshot::{self, Sender};
use tracing::warn;

use super::inject_api::{self, add_response_handler};

// 初始化 api fn(service_id: i64, config: *const u8, config_len: u32, log_level: i32)
type InitApi = unsafe extern "C" fn(i64, *const u8, u32, i32);

// 初始化 api fn()
type FinalizeApi = unsafe extern "C" fn();
// 分发消息 fn(request_id: i64, buffer: *const u8, buffer_len: u32)
type DispatchMessageApi = unsafe extern "C" fn(channel_id: i64, requeest_id: i64, *const u8, u32);
// fn(request_id: i64, buffer: *const u8, buffer_len: u32)
type ResultInApi = unsafe extern "C" fn(i64, *const u8, u32);
// sleep  fn(service_id: i64, milliseconds: u64)
type InjectSleepApi = unsafe extern "C" fn(SleepApi);
// fn(service_id: i64,i64, buffer: *const u8, buffer_size: u32);
type InjectSendMessageApi = unsafe extern "C" fn(SendMessageApi);
// 回调 函数 fn(service_id: i64,i64, msg: *const u8, len: u32) -> i64;
type InjectResponseApi = unsafe extern "C" fn(ResponseApi);
//  fn(service_id: i64,request_id: i64, file_name: *const u8, len: u32);
type InjectFileApi = unsafe extern "C" fn(FileApi);

// fn(service_id: i64,request_id: i64, file_name: *const u8,len: u32, content: *const u8, content_len: u32);
type InjectFileWriteApi = unsafe extern "C" fn(WriteApi);
//  fn(service_id: i64) -> i64
type InjectGenIdApi = unsafe extern "C" fn(GenIDApi);
// fn(service_id: i64,i64, msg: *const u8, len: u32)
type InjectLogOutputApi = unsafe extern "C" fn(OutputLogApi);
//
type InjectLoadServiceApi = unsafe extern "C" fn(LoadServiceApi);
// fn(service_id: i64,i64, msg: *const u8, len: u32)
type InjectUnLoadServiceApi = unsafe extern "C" fn(UnLoadServiceApi);

// fn(service_id: i64,i64, msg: *const u8, len: u32)
type InjectSendHttpRequestApi = unsafe extern "C" fn(SendHttpRequestApi);
// fn(service_id: i64,i64, msg: *const u8, len: u32)
type InjectGetHttpDataApi = unsafe extern "C" fn(GetHttpDataApi);
// fn(service_id: i64,i64, msg: *const u8, len: u32)
type InjectRemoveHttpClientApi = unsafe extern "C" fn(RemoveHttpClientApi);

// fn(service_id: i64, request_id: i64, node_id: i64)
type InjectBuildChannelApi = unsafe extern "C" fn(BuildChannelApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectGetAllLocalServiceApi = unsafe extern "C" fn(GetAllLocalServiceApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectAdChannelIdToRemoteServicesApi = unsafe extern "C" fn(AddChannelIdToRemoteServicesApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectRemoveRemoteServiceAllChannelIdApi =
    unsafe extern "C" fn(RemoveRemoteServiceAllChannelIdApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectSetChannelXRPCPortApi = unsafe extern "C" fn(SetChannelXRPCPortApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectGetXPRCPortApi = unsafe extern "C" fn(GetXPRCPortApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectGetAllConnIdApi = unsafe extern "C" fn(GetAllConnIdApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectGetChannelIdByConnIdApi = unsafe extern "C" fn(GetChannelIdByConnIdApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectGetConnectionApi = unsafe extern "C" fn(GetConnectionApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectPutbackConnectionApi = unsafe extern "C" fn(PutbackConnectionApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectBeginTxApi = unsafe extern "C" fn(BeginTxApi);

type InjectRollbackApi = unsafe extern "C" fn(RollbackApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectCommitApi = unsafe extern "C" fn(CommitApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectQueryApi = unsafe extern "C" fn(QueryApi);

// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectExecuteApi = unsafe extern "C" fn(ExecuteApi);
// fn(service_id: i64, request_id: i64, filter_system: bool)
type InjectIsTableExistApi = unsafe extern "C" fn(IsTableExistApi);

/**
 * service 实例
*/
#[async_trait]
pub trait ServiceIns: Send + Sync {
    fn init(&self, service_id: i64, config: &str, log_level: i32);
    async fn dispatch_message(
        &self,
        channel_id: i64,
        request_id: i64,
        msg: &Vec<u8>,
    ) -> Result<Vec<u8>, Status>;
    fn result_in(&self, request_id: i64, msg: &Vec<u8>);
    fn finalize(&self);
}

pub struct DllService {
    _lib: libloading::Library,
    init_api: Symbol<InitApi>,
    finalize_api: Symbol<FinalizeApi>,
    dispatch_message_api: Symbol<DispatchMessageApi>,
    result_in_api: Symbol<ResultInApi>,
}

//

macro_rules! get_func {
    ($func_name:expr, $lib:expr) => {
        match $lib.get($func_name) {
            Ok(func) => Ok(func),
            Err(err) => Err(Status::error(Self::get_load_dll_err_msg(&err))),
        }
    };
}

macro_rules! get_and_into_raw {
    ($name:expr, $lib:expr, $fn:ty) => {{
        let symbol: libloading::Symbol<'_, $fn> = get_func!($name, $lib)?;
        let symbol: Symbol<$fn> = symbol.into_raw();
        symbol
    }};
}

unsafe impl Send for DllService {}

unsafe impl Sync for DllService {}

impl DllService {
    fn get_load_dll_err_msg(err: &libloading::Error) -> String {
        match err {
            libloading::Error::DlOpen { desc } => format!("{:?}", desc),
            libloading::Error::DlOpenUnknown => "加载失败，未知错误！".to_string(),
            libloading::Error::DlSym { desc } => format!("{:?}", desc),
            libloading::Error::DlSymUnknown => "获取函数失败，未知错误".to_string(),
            libloading::Error::DlClose { desc } => format!("{:?}", desc),
            libloading::Error::DlCloseUnknown => "卸载错误，未知错误".to_string(),
            libloading::Error::LoadLibraryExW { source } => format!("{:?}", source),
            libloading::Error::LoadLibraryExWUnknown => "加载失败，未知错误！".to_string(),
            libloading::Error::GetModuleHandleExW { source } => format!("{:?}", source),
            libloading::Error::GetModuleHandleExWUnknown => "获取句柄错误，未知错误！".to_string(),
            libloading::Error::GetProcAddress { source } => format!("{:?}", source),
            libloading::Error::GetProcAddressUnknown => "获取函数失败，未知错误".to_string(),
            libloading::Error::FreeLibrary { source } => format!("{:?}", source),
            libloading::Error::FreeLibraryUnknown => "卸载错误，未知错误".to_string(),
            libloading::Error::CreateCString { source } => format!("{:?}", source),
            libloading::Error::CreateCStringWithTrailing { source } => format!("{:?}", source),
            _ => "未知错误".to_string(),
        }
    }
    //
    pub fn new(dxc_path: &str) -> Result<Box<Self>, Status> {
        unsafe {
            let lib = match libloading::Library::new(dxc_path) {
                Ok(lib) => lib,
                Err(err) => return Err(Status::error(Self::get_load_dll_err_msg(&err))),
            };

            let init_api = get_and_into_raw!(b"init", lib, InitApi);

            let finalize_api = get_and_into_raw!(b"finalize", lib, FinalizeApi);

            let dispatch_message_api =
                get_and_into_raw!(b"dispatch_message", lib, DispatchMessageApi);

            let result_in_api = get_and_into_raw!(b"result_in", lib, ResultInApi);

            let inject_send_message_api =
                get_and_into_raw!(b"inject_send_message", lib, InjectSendMessageApi);

            let inject_sleep_api = get_and_into_raw!(b"inject_sleep", lib, InjectSleepApi);

            let inject_response_api =
                get_and_into_raw!(b"inject_response_func", lib, InjectResponseApi);

            let inject_is_file_exist_api =
                get_and_into_raw!(b"inject_is_file_exist", lib, InjectFileApi);

            let inject_is_dir_exist_api =
                get_and_into_raw!(b"inject_is_dir_exist", lib, InjectFileApi);

            let inject_create_dir_api = get_and_into_raw!(b"inject_create_dir", lib, InjectFileApi);

            let inject_create_file_api =
                get_and_into_raw!(b"inject_create_file", lib, InjectFileApi);

            let inject_write_file_api =
                get_and_into_raw!(b"inject_write_file", lib, InjectFileWriteApi);

            let inject_file_md5_api = get_and_into_raw!(b"inject_file_md5", lib, InjectFileApi);


            let inject_dxc_dsl_api = get_and_into_raw!(b"inject_dxc_dsl", lib, InjectFileApi);



            let inject_gen_id_api = get_and_into_raw!(b"inject_gen_id", lib, InjectGenIdApi);

            let inject_log_output_api =
                get_and_into_raw!(b"inject_log_output", lib, InjectLogOutputApi);

            //
            let inject_load_service_api =
                get_and_into_raw!(b"inject_load_service", lib, InjectLoadServiceApi);

            let inject_set_channel_xrpc_port = get_and_into_raw!(
                b"inject_set_channel_xrpc_port",
                lib,
                InjectSetChannelXRPCPortApi
            );

            let inject_unload_service_api =
                get_and_into_raw!(b"inject_unload_service", lib, InjectUnLoadServiceApi);

            let inject_send_http_request =
                get_and_into_raw!(b"inject_send_http_request", lib, InjectSendHttpRequestApi);

            let inject_get_http_data =
                get_and_into_raw!(b"inject_get_http_data", lib, InjectGetHttpDataApi);

            let inject_remove_http_client =
                get_and_into_raw!(b"inject_remove_http_client", lib, InjectRemoveHttpClientApi);

            let inject_build_channel =
                get_and_into_raw!(b"inject_build_channel", lib, InjectBuildChannelApi);

            let inject_get_all_local_service = get_and_into_raw!(
                b"inject_get_all_local_service",
                lib,
                InjectGetAllLocalServiceApi
            );

            let inject_add_channel_id_to_remote_services = get_and_into_raw!(
                b"inject_add_channel_id_to_remote_services",
                lib,
                InjectAdChannelIdToRemoteServicesApi
            );

            let inject_remove_remote_services_all_channel_id = get_and_into_raw!(
                b"inject_remove_remote_services_all_channel_id",
                lib,
                InjectRemoveRemoteServiceAllChannelIdApi
            );

            let inject_get_xrpc_port =
                get_and_into_raw!(b"inject_get_xrpc_port", lib, InjectGetXPRCPortApi);

            let inject_get_all_conn_id =
                get_and_into_raw!(b"inject_get_all_conn_id", lib, InjectGetAllConnIdApi);

            let inject_get_channel_id_by_conn_id = get_and_into_raw!(
                b"inject_get_channel_id_by_conn_id",
                lib,
                InjectGetChannelIdByConnIdApi
            );
            // db
            let inject_get_connection =
                get_and_into_raw!(b"inject_get_connection", lib, InjectGetConnectionApi);
            //
            let inject_putback_connection = get_and_into_raw!(
                b"inject_putback_connection",
                lib,
                InjectPutbackConnectionApi
            );

            let inject_begin_tx = get_and_into_raw!(b"inject_begin_tx", lib, InjectBeginTxApi);
            //
            let inject_rollback = get_and_into_raw!(b"inject_rollback", lib, InjectRollbackApi);
            //
            let inject_commit = get_and_into_raw!(b"inject_commit", lib, InjectCommitApi);
            //
            let inject_query = get_and_into_raw!(b"inject_query", lib, InjectQueryApi);
            //
            let inject_execute = get_and_into_raw!(b"inject_execute", lib, InjectExecuteApi);
            //
            let inject_is_table_exist =
                get_and_into_raw!(b"is_table_exist", lib, InjectIsTableExistApi);
            // 出入 api
            inject_send_message_api(inject_api::xport_api::send_message);

            inject_sleep_api(inject_api::sleep);

            inject_is_file_exist_api(inject_api::file_api::is_file_exist);

            inject_is_dir_exist_api(inject_api::file_api::is_dir_exist);

            inject_create_dir_api(inject_api::file_api::create_dir);

            inject_create_file_api(inject_api::file_api::create_file);

            inject_file_md5_api(inject_api::file_api::get_md5);

            inject_dxc_dsl_api(inject_api::file_api::get_dxc_dsl);

            inject_write_file_api(inject_api::file_api::write_file);

            inject_gen_id_api(inject_api::gen_id);

            inject_log_output_api(inject_api::log_output);

            inject_response_api(inject_api::response);

            inject_load_service_api(inject_api::xport_api::load_service);

            inject_unload_service_api(inject_api::xport_api::unload_service);

            inject_build_channel(inject_api::xport_api::build_channel);

            inject_get_all_local_service(inject_api::xport_api::get_all_local_service);

            inject_add_channel_id_to_remote_services(
                inject_api::xport_api::add_channel_id_to_remote_services,
            );

            inject_remove_remote_services_all_channel_id(
                inject_api::xport_api::remove_remote_services_all_channel_id,
            );

            inject_get_xrpc_port(inject_api::xport_api::get_xrpc_port);

            inject_set_channel_xrpc_port(inject_api::xport_api::set_channel_xrpc_port);

            inject_get_all_conn_id(inject_api::xport_api::get_all_conn_id);

            inject_get_channel_id_by_conn_id(inject_api::xport_api::get_channel_id_by_conn_id);

            inject_send_http_request(inject_api::http_api::send_http_request);

            inject_get_http_data(inject_api::http_api::get_http_data);

            inject_remove_http_client(inject_api::http_api::remove_http_client);
            //

            inject_get_connection(inject_api::db_api::get_connection);

            inject_putback_connection(inject_api::db_api::putback_connection);

            inject_begin_tx(inject_api::db_api::begin_tx);

            inject_rollback(inject_api::db_api::rollback);

            inject_commit(inject_api::db_api::commit);

            inject_query(inject_api::db_api::query);

            inject_execute(inject_api::db_api::execute);

            inject_is_table_exist(inject_api::db_api::is_table_exist);

            let api = Box::new(DllService {
                _lib: lib,
                init_api,
                finalize_api,
                dispatch_message_api,
                result_in_api,
            });
            Ok(api)
        }
    }
}

#[async_trait]
impl ServiceIns for DllService {
    // 初始化
    fn init(&self, service_id: i64, config: &str, log_level: i32) {
        unsafe {
            (self.init_api)(service_id, config.as_ptr(), config.len() as u32, log_level);
        }
    }

    // 分发消息
    async fn dispatch_message(
        &self,
        channel_id: i64,
        request_id: i64,
        msg: &Vec<u8>,
    ) -> Result<Vec<u8>, Status> {
        let (sender, receiver) = oneshot::channel::<Vec<u8>>();
        add_response_handler(
            request_id,
            sender,
            Box::new(
                move |sender: Sender<Vec<u8>>, buffer: *const u8, buffer_len: u32| {
                    let slice = unsafe { std::slice::from_raw_parts(buffer, buffer_len as usize) };

                    let vec_buffer = slice.to_vec();
                    let result = sender.send(vec_buffer);
                    if result.is_err() {
                        warn!(
                            "通知消息：{} 返回失败:{:?}",
                            request_id,
                            result.err().unwrap()
                        )
                    }
                },
            ),
        );

        unsafe {
            (self.dispatch_message_api)(channel_id, request_id, msg.as_ptr(), msg.len() as u32);
        }

        let res = receiver.await;

        if res.is_err() {
            return Err(Status::error(format!(
                "返回结果 消息 :{} 错误:{:?}",
                request_id,
                res.err().unwrap()
            )));
        }

        Ok(res.unwrap())
    }
    // 消息进来
    fn result_in(&self, request_id: i64, msg: &Vec<u8>) {
        // 要判断是否有等待的消息

        unsafe {
            (self.result_in_api)(request_id, msg.as_ptr(), msg.len() as u32);
        }
    }
    //

    /**
     * 关闭后就不能用了
     */
    fn finalize(&self) {
        unsafe {
            (self.finalize_api)();
        }
    }
}
